/**
 *
 */
package com.angel.dao.generic.factory;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import com.angel.dao.generic.impl.GenericHibernateDAO;
import com.angel.dao.generic.interfaces.GenericDAO;


/**
 *
 * @author William
 */
public class GenericDAOLocator {

	private List<ClassCodeMapping> customsClassCodes = new ArrayList<ClassCodeMapping>();

	private static GenericDAOLocator instance = null;

	private GenericDAOLocator(){
		super();
	}

	public static synchronized GenericDAOLocator getInstance(){
		if(instance == null){
			instance = new GenericDAOLocator();
		}
		return instance;
	}

	/** Locate a generic DAO object instance for a persistent class and a code class. Persistent class parametized like type T can be some
	 * object, but code class parametized must be a serializable object. It code class identify an unique instance to access data object.
	 *
	 * @param <T> an object class.
	 * @param <Code> a serializable class.
	 * @param persistentClass from domain object. It must be parametized as T.
	 * @param codeClass to identify an unique instance when it is accessed,
	 * @return a generic dao instance object for a persistent and a code class.
	 */
    public <T extends Object,Code extends Serializable> GenericDAO<T, Code> locateGenericDAOFor(Class<T> persistentClass, Class<Code> codeClass) {
    	if(!this.containsGenericDAOFor(persistentClass)){
    		this.registerGenericDAOFor(persistentClass, codeClass);
    	}
        return this.findGenericDAOFor(persistentClass, codeClass);
    }

	public <T extends Object,Code extends Serializable> void registerGenericDAOFor(Class<T> persistentClass, Class<Code> codeClass) {
    	customsClassCodes.add(new ClassCodeMapping(persistentClass, codeClass));
    }

	public <T extends Object> boolean containsGenericDAOFor(Class<T> persistentClass) {
    	boolean contains = false;
    	for(ClassCodeMapping classCodeMapping: customsClassCodes){
    		if(classCodeMapping.isFor(persistentClass)){
    			contains = true;
    			break;
    		}
    	}
    	return contains;
    }

    public <T extends Object,Code extends Serializable> boolean containsGenericDAOFor(Class<T> persistentClass, Class<Code> codeClass) {
    	boolean contains = false;
    	for(ClassCodeMapping classCodeMapping: customsClassCodes){
    		if(classCodeMapping.isFor(persistentClass, codeClass)){
    			contains = true;
    			break;
    		}
    	}
    	return contains;
    }

	@SuppressWarnings("unchecked")
	public <T extends Object,Code extends Serializable> GenericDAO<T, Code> findGenericDAOFor(Class<T> persistentClass, Class<Code> codeClass) {
    	GenericDAO<T, Code> genericDAO = null;
    	for(ClassCodeMapping classCodeMapping: customsClassCodes){
    		if(classCodeMapping.isFor(persistentClass, codeClass)){
    			genericDAO = (GenericDAO<T, Code>) classCodeMapping.buildGenericDAO();
    			return genericDAO;
    		}
    	}
    	return genericDAO;
    }
	
	/** Clean and clear all generic DAOs registers before.
	 * 
	 * @return quantity generic DAOs which was cleared.
	 */
	public Integer cleanGenericDAOs(){
		int quantity = this.customsClassCodes.size();
		this.customsClassCodes.clear();
		return quantity;
	}


    /**
     *
     * @author William
     *
     */
	private class ClassCodeMapping{

		private Class<? extends Object> persistentClass;
		private Class<? extends Serializable> codeClass;
		private GenericDAO<Object, Serializable> genericDAO;


		public <H extends Object, J extends Serializable> ClassCodeMapping(Class<H> persistentClass, Class<J> codeClass){
			this.persistentClass = persistentClass;
			this.codeClass = codeClass;
		}

		@SuppressWarnings("unchecked")
		private GenericDAO<Object, Serializable> buildGenericDAO() {
			if(this.genericDAO == null){
				this.genericDAO = new GenericHibernateDAO(this.getPersistentClass(), this.getCodeClass());
			}
			return genericDAO;
		}

		/**
		 * @return the genericDAO
		 */
		protected GenericDAO<Object, Serializable> getGenericDAO() {
			return genericDAO;
		}

		/**
		 * @return the persistentClass
		 */
		protected Class<? extends Object> getPersistentClass() {
			return persistentClass;
		}

		/**
		 * @return the codeClass
		 */
		protected Class<? extends Serializable> getCodeClass() {
			return codeClass;
		}

		public <H extends Object> boolean isFor(Class<H> persistentClass){
			return this.getPersistentClass() != null && this.getPersistentClass().equals(persistentClass);
		}

		public <H extends Object, J extends Serializable> boolean isFor(Class<H> persistentClass, Class<J> codeClass){
			return 	this.getPersistentClass() != null && this.getPersistentClass().equals(persistentClass) &&
					this.getCodeClass() != null && this.getCodeClass().equals(codeClass);
		}
	}
}
